package tmc.gui;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.util.ResourceBundle;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

import net.miginfocom.swing.MigLayout;

import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.MapContext;
import org.geotools.swing.JMapPane;
import org.geotools.swing.event.MapMouseAdapter;
import org.geotools.swing.event.MapMouseEvent;
import org.geotools.swing.event.MapMouseListener;
import org.geotools.swing.event.MapPaneAdapter;
import org.geotools.swing.event.MapPaneEvent;
import org.geotools.swing.menu.CRSPopupMenu;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/**
 * A status bar to work with JMapPane. It displays the following:
 * <ul>
 * <li>Mouse cursor position (world coordinates)
 * <li>Current display area (world coordinates)
 * <li>Coordinate reference system name
 * <li>Rendering busy indicator
 * </ul>
 * 
 * The CRS name is displayed on a JButton which displays a pop-up menu of CRS
 * options for the map.
 * 
 * @author Michael Bedward
 * @since 2.6
 * @source $URL:
 *         http://svn.osgeo.org/geotools/trunk/modules/unsupported/swing/src
 *         /main/java/org/geotools/swing/StatusBar.java $
 * @version $Id: StatusBar.java 35310 2010-04-30 10:32:15Z jive $
 */
public class TMCStatusBar extends JPanel {
	private static final ResourceBundle stringRes = ResourceBundle.getBundle("org/geotools/swing/Text");

	private JMapPane mapPane;
	private MapContext context;
	private MapMouseListener mouseListener;
	private MapPaneAdapter mapPaneListener;

	private JLabel renderLabel;
	private JLabel coordsLabel;
	private JLabel boundsLabel;
	private JButton crsBtn;
	private CRSPopupMenu crsMenu;

	private ImageIcon busyIcon;
	private static final String BUSY_ICON_IMAGE = "/org/geotools/swing/icons/busy_16.gif";

	/**
	 * Default constructor. {@linkplain #setMapPane} must be called subsequently
	 * for the status bar to receive mouse events.
	 */
	public TMCStatusBar() {
		this(null);
	}

	/**
	 * Constructor. Links the status bar to the specified map pane.
	 * 
	 * @param pane
	 *            the map pane that will send mouse events to this status bar
	 */
	public TMCStatusBar(JMapPane pane) {
		createListeners();
		initComponents();

		if (pane != null) {
			setMapPane(pane);
		}
	}

	/**
	 * Register this status bar to receive mouse events from the given map pane
	 * 
	 * @param newPane
	 *            the map pane
	 * @throws IllegalArgumentException
	 *             if pane is null
	 */
	public void setMapPane(final JMapPane newPane) {
		if (newPane == null) {
			throw new IllegalArgumentException(stringRes.getString("arg_null_error"));
		}

		if (mapPane != newPane) {
			if (mapPane != null) {
				mapPane.removeMouseListener(mouseListener);
			}

			newPane.addMouseListener(mouseListener);
			newPane.addMapPaneListener(mapPaneListener);
			context = newPane.getMapContext();
			mapPane = newPane;

			crsMenu.setMapPane(mapPane);
		}
	}

	/**
	 * Clear the map coordinate display
	 */
	public void clearCoords() {
		coordsLabel.setText("");
	}

	/**
	 * Clear the map bounds display
	 */
	public void clearBounds() {
		boundsLabel.setText("");
	}

	/**
	 * Format and display the coordinates of the given position
	 * 
	 * @param mapPos
	 *            mouse cursor position (world coords)
	 */
	public void displayCoords(DirectPosition2D mapPos) {
		if (mapPos != null) {
			coordsLabel.setText(String.format("  %.2f %.2f", mapPos.x, mapPos.y));
		}
	}

	/**
	 * Display the bounding coordinates of the given envelope
	 * 
	 * @param bounds
	 *            the bounds to display
	 */
	public void displayBounds(Envelope bounds) {
		if (bounds != null) {
			boundsLabel.setText(String.format("Min:%.2f %.2f Span:%.2f %.2f", bounds.getMinimum(0),
					bounds.getMinimum(1), bounds.getSpan(0), bounds.getSpan(1)));
		}
	}

	/**
	 * Display the name of the coordinate reference system
	 * 
	 * @param crs
	 *            the CRS to display
	 */
	public void displayCRS(CoordinateReferenceSystem crs) {
		if (crs == null) {
			crsBtn.setText(stringRes.getString("crs_undefined"));
			crsBtn.setEnabled(false);
			crsMenu.setEnabled(false);
		} else {

			crsBtn.setEnabled(true);
			crsMenu.setEnabled(true);
			crsBtn.setText(crs.getName().toString());
		}
	}

	/**
	 * Creates and sets the layout of components
	 */
	private void initComponents() {
		Rectangle2D rect;
		String constraint;

		LayoutManager lm = new MigLayout("insets 0");
		this.setLayout(lm);

		Font font = Font.decode("Courier-12");

		busyIcon = new ImageIcon(TMCStatusBar.class.getResource(BUSY_ICON_IMAGE));
		renderLabel = new JLabel();
		renderLabel.setHorizontalTextPosition(JLabel.LEADING);
		rect = getFontMetrics(font).getStringBounds("rendering", renderLabel.getGraphics());

		constraint = String.format("gapx 5, width %d!, height %d!", (int) rect.getWidth() + busyIcon.getIconWidth()
				+ renderLabel.getIconTextGap(), (int) Math.max(rect.getHeight(), busyIcon.getIconHeight()) + 6);

		add(renderLabel, constraint);

		coordsLabel = new JLabel();
		Graphics graphics = coordsLabel.getGraphics();
		coordsLabel.setFont(font);

		rect = getFontMetrics(font).getStringBounds("  00000000.000 00000000.000", graphics);

		constraint = String.format("width %d!, height %d!", (int) rect.getWidth() + 10, (int) rect.getHeight() + 6);

		add(coordsLabel, constraint);

		boundsLabel = new JLabel();
		boundsLabel.setFont(font);

		rect = getFontMetrics(font).getStringBounds("Min: 00000000.000 00000000.000 Span: 00000000.000 00000000.000",
				graphics);

		constraint = String.format("width %d!, height %d!", (int) rect.getWidth() + 10, (int) rect.getHeight() + 6);

		add(boundsLabel, constraint);

		crsBtn = new JButton(stringRes.getString("crs_undefined"));
		crsBtn.setFont(font);
		crsBtn.setEnabled(false);

		rect = getFontMetrics(font).getStringBounds("X", graphics);

		constraint = String.format("height %d!", (int) rect.getHeight() + 6);

		crsBtn.setToolTipText(stringRes.getString("tool_tip_statusbar_crs"));
		crsMenu = new CRSPopupMenu();
		crsMenu.setEnabled(false);
		crsBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				crsMenu.show(crsBtn, 0, 0);
			}
		});

		add(crsBtn, constraint);
	}

	/**
	 * Initialize the mouse and map bounds listeners
	 */
	private void createListeners() {
		mouseListener = new MapMouseAdapter() {

			@Override
			public void onMouseMoved(MapMouseEvent ev) {
				displayCoords(ev.getMapPosition());
			}

			@Override
			public void onMouseExited(MapMouseEvent ev) {
				clearCoords();
			}
		};

		mapPaneListener = new MapPaneAdapter() {

			@Override
			public void onDisplayAreaChanged(MapPaneEvent ev) {
				ReferencedEnvelope env = mapPane.getDisplayArea();
				if (env != null) {
					displayBounds(env);
					displayCRS(env.getCoordinateReferenceSystem());
				} else {
					crsBtn.setEnabled(false);
					crsMenu.setEnabled(false);
				}
			}

			@Override
			public void onResized(MapPaneEvent ev) {
				ReferencedEnvelope env = mapPane.getDisplayArea();
				if (env != null) {
					displayBounds(env);
					displayCRS(env.getCoordinateReferenceSystem());
				} else {
					crsBtn.setEnabled(false);
					crsMenu.setEnabled(false);
				}
			}

			@Override
			public void onRenderingStarted(MapPaneEvent ev) {
				renderLabel.setText("rendering");
				renderLabel.setIcon(busyIcon);
			}

			@Override
			public void onRenderingStopped(MapPaneEvent ev) {
				renderLabel.setText("");
				renderLabel.setIcon(null);
			}

			@Override
			public void onRenderingProgress(MapPaneEvent ev) {
				float progress = ((Number) ev.getData()).floatValue();
				System.out.println("render progress: " + progress);
			}


		};
	}

}
